package com.wanghua.spring.filter;

import com.wanghua.spring.filter.exception.AccountException;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
 * Filter 与 Listener 都属于 Servlet 的规范。
 * 权限验证 Filter。
 * 如果只检查是否登录，则只检查 session 或 cookie 中是否有信息即可。如果要检查权限，则需要一个 URI 与权限角色的检查规则。
 * 检查规则一般存储在配置文件或数据库中。
 */
public class PrivilegeFilter implements Filter {
    private Properties pp = new Properties();  //java的properties文件需要放到classpath下面，这样程序才能读取到，有关classpath实际上就是java类或者库的存放路径，在java工程中，properties放到class文件一块。在web应用中，最简单的方法是放到web应用的WEB- INF\classes目录下即可，也可以放在其他文件夹下面，这时候需要在设置classpath环境变量的 时候，将这个文件夹路径加到 classpath变量中，这样也也可以读取到。在此，你需要对classpath有个深刻理解，classpath绝非系统中刻意设定的那个系统环境变 量，WEB-INF\classes其实也是，java工程的class文件目录也是。

    public void init(FilterConfig config) throws ServletException {
        String file = config.getInitParameter("file");  //web.xml中的filter节点的"init-param"参数
        String realPath = config.getServletContext().getRealPath(file);
        try {
            pp.load(new FileInputStream(realPath));  //加载所有的权限配置
        } catch (Exception e) {
            config.getServletContext().log("读取权限控制文件失败。", e);
        }
    }

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;

        //获取相对路径
        String requestURI = request.getRequestURI().replace(request.getContextPath() + "/", "");

        //带参数的相对路径，格式如：list.do?action=save
        String action = req.getParameter("action");
        action = action == null ? "" : action;
        String uri = requestURI + "?action=" + action;

        //给带参数的相对路径设置角色，如果当前 session 中没有角色信息，则使用 guest。
        String role = (String) request.getSession(true).getAttribute("role");
        role = role == null ? "guest" : role;

        boolean authentificated = false;

        //检查该用户角色是否有权限访问该 uri。
        for (Object obj : pp.keySet()) {
            String key = ((String) obj);
            //使用正则表达式验证，需要将 ? . 替换一下，并将通配符 * 处理一下。最终格式如：list\.do\?action=save
            if (uri.matches(key.replace("?", "\\?").replace(".", "\\.").replace("*", ".*"))) {
                if (role.equals(pp.get(key))) {
                    authentificated = true;
                    break;
                }
            }
        }
        if (!authentificated) {
            throw new RuntimeException(new AccountException("您无权访问该页面。请以合适的身份登陆后查看。"));
        }

        chain.doFilter(req, res);  //通过了验证执行 doFilter，执行下一个 filter 或 servlet。
    }

    public void destroy() {
        pp = null;
    }
}